﻿using Hopeless.Metaphysical;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UCS.XNA.Utils;
using UCS.XNA.Utils.Animation;

namespace Hopeless.Physical
{
    internal class Hero : ITranslateAnimatable, IScalable
    {
        private const int BODY_WIDTH = 15;
        private const int BODY_HEIGHT = 5;
        private const int HEAD_RADIUS = 5;

        private Rectangle _headSpace;
        private Texture2D _bodyTexture;
        private Texture2D _headTexture;
        private Random _random = new Random();

        private Rectangle _bodySpace;
        public Rectangle BodySpace
        {
            get { return _bodySpace; }
        }

        private int _life = 100;
        public int Life
        {
            get { return _life; }
        }

        private Sword _sword;
        internal Sword Sword
        {
            get { return _sword; }
            set
            {
                _sword = value;
                this.InitializeSwordPosition();
            }
        }

        private Enhancement _critical;
        internal Enhancement Critical
        {
            get { return _critical; }
        }

        private Enhancement _knockback;
        internal Enhancement Knockback
        {
            get { return _knockback; }
        }

        private Enhancement _speed;
        internal Enhancement Speed
        {
            get { return _speed; }
        }

        private Enhancement _protection;
        internal Enhancement Protection
        {
            get { return _protection; }
        }

        private Vector2 _position;
        public Vector2 Position
        {
            get { return _position; }
            set
            {
                if (!this.CheckTerrainCollision(value) && !this.CheckAbyssCollision(value))
                    return;

                _position = value;
                this.InitializeBodyHead();
            }
        }

        public Vector2 RelativeOrigin
        {
            get { return new Vector2(BODY_WIDTH / 2f, BODY_HEIGHT / 2f); }
        }

        private float _scale = 1f;
        public float Scale
        {
            get { return _scale; }
            set
            {
                _scale = value;
                this.InitializeBodyHead();
                this.InitializeSwordPosition();
            }
        }

        private float _facingAngle = 0;
        public float FacingAngle
        {
            get { return _facingAngle; }
            set
            {
                _facingAngle = value;
                if (_facingAngle > Math.PI * 2)
                    _facingAngle = (float)(Math.PI * 2 - _facingAngle);
                else if (_facingAngle < 0)
                    _facingAngle = (float)(Math.PI * 2 + _facingAngle);

                if (!_sword.Swinging)
                    _sword.FacingAngle = _facingAngle;

                this.InitializeSwordPosition();
            }
        }

        private bool _falling = false;
        public bool Falling
        {
            get { return _falling; }
            set { _falling = value; }
        }

        public bool IsDead
        {
            get { return _life <= 0; }
        }

        public void Initialize()
        {
            _life = 100;
            _critical = new Enhancement("Critical", 5);
            _knockback = new Enhancement("Knockback", 5);
            _speed = new Enhancement("Speed", 5);
            _protection = new Enhancement("Protection", 5);
            _bodyTexture = GameResources.Content.Load<Texture2D>("Hero_Body");
            _headTexture = GameResources.Content.Load<Texture2D>("Hero_Head");

            this.InitializeBodyHead();
        }

        public void Update(GameTime gameTime)
        {
            if (_sword.Swinging)
            {
                if (Math.Abs(_sword.FacingAngle - _facingAngle) < 0.1f)
                {
                    _sword.StopSwing();
                    _sword.FacingAngle = _facingAngle;
                }
                else
                {
                    var step = 0.09f;
                    if (Math.Abs(_sword.FacingAngle - _facingAngle) < step)
                        _sword.FacingAngle = _facingAngle;
                    else
                        _sword.FacingAngle += step;
                }
            }
        }

        public void Draw(GameTime gameTime)
        {
            GameResources.SpriteBatch.Begin();

            GameResources.SpriteBatch.Draw(_bodyTexture, _bodySpace, null, Color.White, _facingAngle, new Vector2(_bodyTexture.Width / 2, _bodyTexture.Height / 2), SpriteEffects.None, 0);
            GameResources.SpriteBatch.Draw(_headTexture, _headSpace, null, Color.White, 0, new Vector2(_headTexture.Width / 2, _headTexture.Height / 2), SpriteEffects.None, 0);

            GameResources.SpriteBatch.End();
        }

        public void SwingSword()
        {
            if (!_sword.Swinging)
            {
                _sword.FacingAngle = (float)(_facingAngle - Math.PI / 2f);
                var damage = 10f;
                var criticalChance = _critical.Level / 10f;
                var criticalHit = _random.NextDouble() <= criticalChance;
                if (criticalHit)
                    damage += 10f;

                var knockbackForce = 3f + 0.6f * _knockback.Level;
                
                _sword.Swing(damage, knockbackForce);
            }
        }

        private void InitializeBodyHead()
        {
            _bodySpace = new Rectangle((int)_position.X, (int)_position.Y, (int)(BODY_WIDTH * _scale), (int)(BODY_HEIGHT * _scale));
            _headSpace = new Rectangle((int)_position.X, (int)_position.Y, (int)(HEAD_RADIUS * _scale), (int)(HEAD_RADIUS * _scale));
        }

        private void InitializeSwordPosition()
        {
            var sin = Math.Sin(_facingAngle);
            var cos = Math.Cos(_facingAngle);

            var pos = _position - new Vector2((float)cos, (float)sin) * _bodySpace.Width / 2;

            _sword.Position = pos;
        }

        private bool CheckTerrainCollision(Vector2 position)
        {
            var terrain = HopelessGame.Universe.Terrain;
            return terrain.Space.Contains((int)position.X, (int)position.Y);
        }

        private bool CheckAbyssCollision(Vector2 position)
        {
            var abyss = HopelessGame.Universe.Abyss;
            return abyss.Space.Contains((int)position.X, (int)position.Y);
        }

        public void Walk(Vector2 direction)
        {
            if (_falling)
                return;

            var speed = 1f + _speed.Level * 0.2f;
            var off = direction * speed;
            var nextPosition = this.Position + off;

            this.Position = nextPosition;
        }

        public void TakeDamage(int damage)
        {
            damage -= (int)(damage * (_protection.Level / 10f));

            _life -= damage;

            BloodSpill bloodSpill;
            if (damage < 15)
                bloodSpill = new BloodSpill(_random.Next(2, 6));
            else
                bloodSpill = new BloodSpill(_random.Next(7, 11));

            bloodSpill.Initialize();
            bloodSpill.Position = _position;
            bloodSpill.Scale = _scale;

            var ang = _facingAngle - Math.PI / 2f;
            var cos = Math.Cos(ang);
            var sin = Math.Sin(ang);
            var dir = new Vector2((float)cos, (float)sin);
            Physics.Animations.Add(new OffsetTranslateAnimation(bloodSpill, dir, 15, 2));

            HopelessGame.Universe.BloodSpills.Add(bloodSpill);
        }
    }
}
